package com.apobates.jforum.grief.aurl.parser;

import com.apobates.jforum.grief.aurl.core.AUrlMetaBuilder;
import com.apobates.jforum.grief.aurl.expression.UrlExprLexer;
import com.apobates.jforum.grief.aurl.expression.UrlExprParser;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.dfa.DFA;

import java.util.BitSet;
import java.util.Optional;

/**
 * 使用Antlr 4解析URL
 */
public class AntlrParser implements AUrlParser {
    @Override
    public AUrlMetaBuilder parse(String url) throws AUrlParserException {
        CharStream cs = CharStreams.fromString(url);
        UrlExprLexer lexer = new UrlExprLexer(cs);
        lexer.removeErrorListeners();
        lexer.addErrorListener(new BaseErrorListener(){
            @Override
            public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
                throw new AUrlParserException("词法分析失败, 原因: 行号= "+line+", 字符位置= "+charPositionInLine+", 消息= "+msg);
            }
        });
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        UrlExprParser parser = new UrlExprParser(tokens);
        parser.removeErrorListeners();
        parser.addErrorListener(new BaseErrorListener(){
            @Override
            public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
                throw new AUrlParserException("解析失败, 原因: 行号= "+line+", 字符位置= "+charPositionInLine+", 消息= "+msg);
            }
        });
        try {
            UrlExprParser.UrlContext urlContext = parser.url();
            UrlExprParser.UriContext uri = urlContext.uri();
            //
            Optional<String> schema = Optional.ofNullable(uri.scheme()).map(RuleContext::getText);
            Optional<String> host = Optional.ofNullable(uri.host()).map(RuleContext::getText);
            Optional<String> port = Optional.ofNullable(uri.port()).map(RuleContext::getText);
            Optional<String> path = Optional.ofNullable(uri.path()).map(ele -> addPrefixSymbol(ele.getText(), "/"));
            Optional<String> query = Optional.ofNullable(uri.query()).map(ele -> removePrefixSymbol(ele.getText(), "?"));
            Optional<String> userinfo = Optional.ofNullable(uri.login()).map(RuleContext::getText);
            Optional<String> fragment = Optional.ofNullable(uri.frag()).map(ele -> removePrefixSymbol(ele.getText(), "#"));

            return AUrlMetaBuilder.getInstance().scheme(schema).userInfo(userinfo).host(host).port(port).path(path).query(query).fragment(fragment);
        }catch (RecognitionException e){
            throw new AUrlParserException(e.getMessage());
        }
    }

    /**
     * 移除目标的前置符号(存在时)
     * @param target 目标字符串
     * @param prefixSymbol 前置符号
     * @return
     */
    private static String removePrefixSymbol(String target, String prefixSymbol){
        if(target.startsWith(prefixSymbol)){
            return target.substring(1);
        }
        return target;
    }

    /**
     * 添加目标的前置符号(不存在时)
     * @param target 目标字符串
     * @param prefixSymbol 前置符号
     * @return
     */
    private static String addPrefixSymbol(String target, String prefixSymbol){
        if(!target.startsWith(prefixSymbol)){
            return prefixSymbol+target;
        }
        return target;
    }
}
